package com.example.adminframework.util;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.Security;

/**
 * @Date 2020.12.02
 * @Description 用于Aes加解密的工具类
 */
@Component
public class AesUtil {
    @Autowired
    private Environment env;

    //加密key
    private static String encryptAesKey = "f4k9f5w7f8g4er26";

    //偏移量
    private static String encryptAesIV = "5e8y6w45ju8w9jq8";

    //编码
    private static String encryptAesEncode = "UTF-8";

    //算法
    private static String encryptAesAlgorithm = "AES";

    //加密算法
    private static String encryptAesCipherAlgorithm = "AES/CBC/PKCS5Padding";

    @PostConstruct
    public void loadAesParameter(){
        String encryptAesKey = env.getProperty("encrypt.Aes.key");
        if (StringUtils.isNoneBlank(encryptAesKey))
            AesUtil.encryptAesKey = encryptAesKey;
        String encryptAesIv = env.getProperty("encrypt.Aes.IV");
        if (StringUtils.isNoneBlank(encryptAesIv))
            AesUtil.encryptAesIV = encryptAesIv;
        String encryptAesEncode = env.getProperty("encrypt.Aes.encode");
        if (StringUtils.isNoneBlank(encryptAesEncode))
            AesUtil.encryptAesEncode = encryptAesEncode;
        String encryptAesAlgorithm = env.getProperty("encrypt.Aes.algorithm");
        if (StringUtils.isNoneBlank(encryptAesAlgorithm))
            AesUtil.encryptAesAlgorithm = encryptAesAlgorithm;
        String encryptAesCipherAlgorithm = env.getProperty("encrypt.Aes.cipher.algorithm");
        if (StringUtils.isNoneBlank(encryptAesCipherAlgorithm))
            AesUtil.encryptAesCipherAlgorithm = encryptAesCipherAlgorithm;
    }
//    @Value("${encrypt.Aes.key}")
//    public void setEncryptAesKey(String key) {
//        encryptAesKey = key;
//    }
//
//    @Value("${encrypt.Aes.IV}")
//    public void setEncryptAesIV(String IV) {
//        encryptAesIV = IV;
//    }
//
//    @Value("${encrypt.Aes.encode}")
//    public void setEncryptAesEncode(String encode) {
//        encryptAesEncode = encode;
//    }
//
//    @Value("${encrypt.Aes.algorithm}")
//    public void setEncryptAesAlgorithm(String algorithm) {
//        encryptAesAlgorithm = algorithm;
//    }
//
//    @Value("${encrypt.Aes.cipher.algorithm}")
//    public void setEncryptAesCipherAlgorithm(String cipherAlgorithm) {
//        encryptAesCipherAlgorithm = cipherAlgorithm;
//    }
    private static final Logger LOG = LoggerFactory.getLogger(AesUtil.class);

    /**
     * AES加密
     *
     * @param value
     * @param key
     * @return
     */
    public static byte[] encryptByKeyAndIv(String value, String key) {
        try {
            Security.addProvider(new sun.security.provider.Sun());
            KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者
//            kgen.init(128, new SecureRandom(key.getBytes()));// 利用用户密码作为随机数初始化出

            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", new sun.security.provider.Sun());
            secureRandom.setSeed(key.getBytes());
            kgen.init(128, secureRandom);

            //加密没关系，SecureRandom是生成安全随机数序列，key.getBytes()是种子，只要种子相同，序列就一样，所以解密只要有password就行
            SecretKey secretKey = kgen.generateKey();// 根据用户密码，生成一个密钥
            byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥，如果此密钥不支持编码，则返回
            SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥
            Cipher cipher = Cipher.getInstance("AES");// 创建密码器
            byte[] byteContent = value.getBytes("utf-8");
            cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);// 初始化为加密模式的密码器
            byte[] result = cipher.doFinal(byteContent);// 加密
            return result;
        } catch (NoSuchPaddingException e) {
            LOG.error(e.getMessage(), e);
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            LOG.error(e.getMessage(), e);
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            LOG.error(e.getMessage(), e);
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            LOG.error(e.getMessage(), e);
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            LOG.error(e.getMessage(), e);
            e.printStackTrace();
        } catch (BadPaddingException e) {
            LOG.error(e.getMessage(), e);
            e.printStackTrace();
        }
        return null;
    }

    /**
     * AES解密 参数是二进制数组 + key
     *
     * @param value
     * @param key
     * @return
     */
    public static byte[] desCrypt(byte[] value, String key) {
        try {
            Security.addProvider(new sun.security.provider.Sun());
            KeyGenerator kgen = KeyGenerator.getInstance("AES");// 创建AES的Key生产者
//            kgen.init(128, new SecureRandom(key.getBytes()));

            SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG", new sun.security.provider.Sun());
            secureRandom.setSeed(key.getBytes());
            kgen.init(128, secureRandom);

            SecretKey secretKey = kgen.generateKey();// 根据用户密码，生成一个密钥
            byte[] enCodeFormat = secretKey.getEncoded();// 返回基本编码格式的密钥
            SecretKeySpec secretKeySpec = new SecretKeySpec(enCodeFormat, "AES");// 转换为AES专用密钥
            Cipher cipher = Cipher.getInstance("AES");// 创建密码器
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);// 初始化为解密模式的密码器
            byte[] result = cipher.doFinal(value);
            return result; // 明文
        } catch (NoSuchAlgorithmException e) {
            LOG.error(e.getMessage(), e);
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            LOG.error(e.getMessage(), e);
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            LOG.error(e.getMessage(), e);
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            LOG.error(e.getMessage(), e);
            e.printStackTrace();
        } catch (BadPaddingException e) {
            LOG.error(e.getMessage(), e);
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 将二进制密文转再次换成可识别字符串 返回的是16进制
     *
     * @param value
     * @return
     */
    public static String parseEncrypt(String value, String key) {
        byte[] encrypt = encryptByKeyAndIv(value, key);
//        String s = ParseSystemUtil.parseByte2HexStr(encrypt);
        String s = Base64Util.byte2Base64StringFun(encrypt);
        return s;
    }

    /**
     * 返回解密后的内容
     *
     * @param value
     * @param key
     * @return
     */
    public static String parseDesEncrypt(String value, String key) {
//        byte[] bytes = ParseSystemUtil.parseHexStr2Byte(value);
        byte[] bytes = Base64Util.base64String2ByteFun(value);
        try {
            return new String(desCrypt(bytes, key), encryptAesEncode);
        } catch (UnsupportedEncodingException e) {
            LOG.error(e.getMessage(), e);
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 带偏移量的解密
     *
     * @param data
     * @return
     */
    public static String decryptByKeyAndIV(String data) {
        try {
            if (StringUtils.isBlank(data)) {
                return null;
            }
            Cipher cipher = Cipher.getInstance(encryptAesCipherAlgorithm);
            SecretKeySpec skeySpec = new SecretKeySpec(encryptAesKey.getBytes(encryptAesEncode), encryptAesAlgorithm);
            IvParameterSpec iv = new IvParameterSpec(encryptAesIV.getBytes());//使用CBC模式，需要一个向量iv，可增加加密算法的强度
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] buffer = Base64Util.base64String2ByteFun(data);
            byte[] encrypted = cipher.doFinal(buffer);
            return new String(encrypted, encryptAesEncode);//此处使用BASE64做转码。
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    /**
     * 加密
     *
     * @param data
     * @return String
     * @throws Exception
     * @date 2018年6月28日下午2:50:35
     */
    public static String encryptByKeyAndIv(String data) throws Exception {
        Cipher cipher = Cipher.getInstance(encryptAesCipherAlgorithm);
        SecretKeySpec skeySpec = new SecretKeySpec(encryptAesKey.getBytes(encryptAesEncode), encryptAesAlgorithm);
        IvParameterSpec iv = new IvParameterSpec(encryptAesIV.getBytes());//使用CBC模式，需要一个向量iv，可增加加密算法的强度
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        byte[] encrypted = cipher.doFinal(data.getBytes(encryptAesEncode));
        return Base64Util.byte2Base64StringFun(encrypted);//此处使用BASE64做转码。
    }

    /**
     * 因spring 加载顺序问题  数据库用到了解密
     * 数据库init 会比 静态注入属性加载早
     * 所以强制 使用非静态工具方法 spring会强制先加载这边
     * @param param
     * @return
     */
    public String decryptByKeyAndIVWithDB(String param) {
        String decrypt = AesUtil.decryptByKeyAndIV(param);
        return decrypt;
    }


}
